package jp.co.nes.awssupport.common.base;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import jp.co.nes.awssupport.common.utility.AppInfoUtil;
import jp.co.nes.awssupport.common.utility.DbConnUtil;

/**
 * Lambda関数を作成する際に使用する抽象クラスです。
 *
 * @param <I> 入力情報
 * @param <O> 出力情報
 */
public abstract class AbstractRequestHandler<I, O> implements RequestHandler<I, O> {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(AbstractRequestHandler.class);

    /** コンテキスト */
    protected Context context;

    /*
     * (非 Javadoc)
     * @see com.amazonaws.services.lambda.runtime.RequestHandler#handleRequest(java.lang.Object, com.amazonaws.services.lambda.runtime.Context)
     */
    @Override
    public O handleRequest(I input, Context context) {
        long start = System.currentTimeMillis();
        AppInfoUtil.reload();
        String classSimpleName = null;
        boolean isSuccess = false;
        O output = null;
        try {
            classSimpleName = getClass().getSimpleName();
            if (StringUtils.isEmpty(classSimpleName)) {
                // 無名クラスだった場合、getSimpleName()の戻り値が空なので、getName()の値を設定する。
                classSimpleName = getClass().getName();
            }
            logger.info("*** " + classSimpleName + " start ***");

            if (input == null) {
                logger.info("input is null");
            } else {
                logger.info("input=" + input.toString());
            }

            this.context = context;
            output = handleRequest(input);
            isSuccess = true;
        } catch (Throwable e) {
            failed(e, input, output);
        } finally {
            if (output == null) {
                logger.info("output is null");
            } else {
                logger.info("output=" + output.toString());
            }

            finish(isSuccess, input, output);
            if (isSuccess) {
                long time = System.currentTimeMillis() - start;
                logger.info("*** " + classSimpleName + " end(time=" + time + ") ***");
            }
        }
        return output;
    }

    /**
     * 処理を実行します。
     *
     * @param input 入力情報
     * @return 出力情報
     */
    protected abstract O handleRequest(I input);

    /**
     * 例外が発生した時の処理を実行します。
     *
     * @param e 発生した例外
     * @param input 入力情報
     * @param output 出力情報
     */
    protected void failed(Throwable e, I input, O output) {
        logger.error(e.getMessage(), e);

        if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        } else if (e instanceof Error){
            throw (Error) e;
        }
        throw new CommonException(e);
    }

    /**
     * 終了処理です。
     *
     * @param isSuccess true: 処理が成功, false: 処理が失敗
     * @param input 入力情報
     * @param output 出力情報
     */
    protected void finish(boolean isSuccess, I input, O output) {
        if (isUseRds()) {
            try {
                if (isSuccess) {
                    DbConnUtil.commitAll();
                } else {
                    DbConnUtil.rollbackAll();
                }
            } finally {
                DbConnUtil.closeAll();
            }
        }
    }

    /**
     * RDSを使用しているかどうかを返却します。
     *
     * @return true: 使用, false: 未使用
     */
    protected boolean isUseRds() {
        boolean result = false;
        String jdbcDriver = AppInfoUtil.getVal("Rds", "JdbcDriver");
        if (jdbcDriver != null) {
            result = true;
        }
        return result;
    }
}
